<?php
m2import('phpinchains/parser/el/ELGrammar.lalr.php');
m2import('phpinchains/parser/el/ELLexer.php');
m2import('phpinchains/parser/el/tokenClasses.php');
m2import('phpinchains/parser/el/ELCallback.php');
m2import('phpinchains/parser/psp/PSPLexer.php');
m2import('phpinchains/parser/psp/PSPParser.php');
m2import('phpinchains/parser/ParserMachine.php');
m2import('phpinchains/tagsupport/URL.php');
m2import('phpinchains/util/DbUtil.php');

/**
 * Super class of all actions dispatched by the ActionDispatcherFilter
 *
 */
class phpinchains_WebAction
{

    var $basedir;
    var $env;
    /**
     *
     * @var ActionDispatcherFilter Dispatcher reference
     */
    var $dispatcher;
    var $actionform;
    var $useReturnValue = true;
    var $processingHalted = false;

    /**
     * Pre processing
     * 
     * Override this method for pre processing of all methods
     *
     * @param env $env
     * @param phpinchains_ViewMapping $viewmapping
     * @param phpinchains_ActionForm $actionform
     */
    function _preProcess ($methodname, env $env, phpinchains_ViewMapping $viewmapping, phpinchains_ActionForm $actionform) {
    }
    
    /**
     * Post processing
     * 
     * Override this method for post processing of all methods
     *
     * @param env $env
     * @param phpinchains_ViewMapping $viewmapping
     * @param phpinchains_ActionForm $actionform
     */
    function _postProcess ($methodname, env $env, phpinchains_ViewMapping $viewmapping, phpinchains_ActionForm $actionform) {
    }

    /**
     * Halt processing of the called action method
     * 
     * Usually called in _preProcessing. Has to be called before the executed method was called.
     *
     */
    function haltProcessing () {
    	$this->processingHalted = true;
    }
    
    /**
     * Enable or disable sec code in c:url and h:form
     * 
     * Enable makes c:url and h:form to add
     *
     * @param boolean $bool
     */
    function setEnableSecCode ($bool) {
    	$run = &phpinchains_RuntimeConfig::getInstance();
    	$run->set("sec_code_enabled", $bool); 
    }
        
    /**
     * Called by action dispatcher
     * 
     * @param dispatcher ActionDispatcherFilter The application controller
     * @param env env Envionment wrapper to reduce coupling with so called magic vars
     * @param basedir string Basedir of the module
     * @param viewmapping ViewMapping ViewMapping instance which provides the configured view mapping
     * @param actionform ActionForm The incoming action form with the request parameters
     * @param method string Method name of the action method if one is specified
     * @param bool                   $outsidecall  Set true at initial call of this method
     * @return string Name of a php or psp page (with extension) or a view mapping
     *      to forward to. If an empty string is returned then a mapping with same name as method
     *      is called (filterParam view-by-methodname == true) or nothing happens (else). filterParam
     *      no-view-error prevents an error.
     */
     /**
      * Called by action dispatcher
      *
      * @param ActionDispatcherFilter  $dispatcher  The application controller
      * @param env                     $env         Envionment wrapper to reduce coupling with so called magic vars
      * @param string                  $basedir     Basedir of the module
      * @param phpinchains_ViewMapping $viewmapping ViewMapping instance which provides the configured view mapping
      * @param phpinchains_ActionForm  $actionform  The incoming action form with the request parameters
      * @param string                  $method      Method name of the action method if one is specified
      * @param bool                    $outsidecall Is the action called from the outside
      * @return
      */
    function executeAction(& $dispatcher, & $env, $basedir, & $viewmapping, & $actionform, $method = '', $outsidecall = "")
    {
    	$ctx = & phpinchains_ApplicationContext::getInstance();
		$ctx->debug('WebAction::executeAction method:'.($method ? $method : 'execute'));
    	$this->dispatcher = & $dispatcher;
        $this->env = & $env;
        if ($outsidecall)
            $this->env->cycleAttributes();
        $this->basedir = $basedir;
        $this->viewmapping = & $viewmapping;
        $this->actionform = & $actionform;
        $retVal = null;
        $this->_preProcess(($method?$method:'execute'), $env, $viewmapping, $actionform, $outsidecall);
        if ($this->processingHalted) {
        	$this->setUseReturnValue(false);
        	return;
        }
        if ($method) {
            $retVal = $this->$method ($env, $viewmapping, $actionform);
        } else {
            $retVal = $this->execute($env, $viewmapping, $actionform);
        }
        $this->_postProcess(($method?$method:'execute'), $env, $viewmapping, $actionform);
        return $retVal;
    }

    /**
     * The method to override by the actions
     *
     * @param env env Envionment wrapper to reduce coupling with so called magic vars
     * @param viewmapping ViewMapping ViewMapping instance which provides the configured view mapping
     * @param actionform ActionForm The incoming action form with the request parameters
     * @return string Name of a php or psp page (with extension) or a view mapping
     *      to forward to. If an empty string is returned then a mapping with same name as method
     *      is called (filterParam view-by-methodname == true) or nothing happens (else). filterParam
     *      no-view-error prevents an error.
     */
    function execute(& $env, & $viewmapping, & $actionform)
    {
        $this->index(& $env, & $viewmapping, & $actionform);
    }

    
    /**
     * <<abstract>>
     * The method to override by the actions. Default method to be called.
     *
     * @param env env Envionment wrapper to reduce coupling with so called magic vars
     * @param viewmapping ViewMapping ViewMapping instance which provides the configured view mapping
     * @param actionform ActionForm The incoming action form with the request parameters
     * @return string Name of a php or psp page (with extension) or a view mapping
     *      to forward to. If an empty string is returned then a mapping with same name as method
     *      is called (filterParam view-by-methodname == true) or nothing happens (else). filterParam
     *      no-view-error prevents an error.
     */
    function index(& $env, & $viewmapping, & $actionform)
    {
        print "<strong>The index method of WebAction must be overridden if it is configured</strong>";
    }

    
    /**
     * Forwarding to a view. A view is either a flat php file that has access to the 
     * variables $env with the environment wrapper and $actionform, somthing like a bean
     * in Java.
     * 
     * If this method is called the return value of the execute method is ignored.
     * 
     * @param view string Name of the view php file (without file extension; the file
     *              must have extension .inc.php) or the view PSP file (with extension 
     *              .psp; file has to have the same name)
     * @param phpinchains_ActionForm actionform Actionform object to transport data to the view
     */
    function forward($view, $actionform)
    {
        $this->useReturnValue = false;
        $this->dispatcher->forward($view, $actionform);
    }

    /**
     * Setting the response output. If one wants to bypass views and wants to set the output by himself.
     * 
     * If this method is called the return value of the execute method is ignored.
     * 
     * @param string output The string to output as response.
     */
    function setResponse($output)
    {
        $this->setUseReturnValue(false);
    	$ctx = & phpinchains_ApplicationContext::getInstance();
        $out = & $ctx->getOutputWriter();
        $out->write($output);
    }

    /**
     * Setting the response output as data structure and output it as JSON. 
     *
     * If one wants to bypass views and wants to set the output by himself.
     * If this method is called the return value of the execute method is ignored.
     * 
     * @param mixed  output  An object or array to convert to JSON and output.
     * @param mixed  onerror An object or array to convert to JSON and output if there are errors with 'output'.
     */
    function setJSONResponse($output, $encoding = 'utf-8')
    {
        m2import("Zend/Json.php");
        header("Content-Type: text/html; charset=".$encoding);
        $this->setUseReturnValue(false);
        $ctx = & phpinchains_ApplicationContext::getInstance();
        $out = & $ctx->getOutputWriter();
        if ($encoding != 'utf-8') {
        	Zend_Json::$useBuiltinEncoderDecoder = true;
        }
        $enc = Zend_Json::encode($output);
        $out->write($enc);
    }
    
    /**
     * Setting the response output as data structure and output it as XML. 
     *
     * If one wants to bypass views and wants to set the output by himself.
     * If this method is called the return value of the execute method is ignored.
     * 
     * @param mixed  output  An object or array to convert to XML and output.
     * @param mixed  onerror An object or array to convert to XML and output if there are errors with 'output'.
     */
    function setXMLResponse($output, $rootelement = "output",  $encoding = 'utf-8')
    {
        m2import("phpinchains/util/XMLSerializer.php");
        header("Content-Type: application/xml; charset=".$encoding);
        $this->setUseReturnValue(false);
        $ctx = & phpinchains_ApplicationContext::getInstance();
        $out = & $ctx->getOutputWriter();
        $enc = phpinchains_util_XMLSerializer::serialize($output, $rootelement);
        $out->write($enc);
    }
    
    /**
     * Setting the response output as data structure and output it as XML. Supports similar structures as E4X. 
     *
     * If one wants to bypass views and wants to set the output by himself.
     * If this method is called the return value of the execute method is ignored.
     * String indexes are treated as tag names. Indexes prefixed with '@' are treated as attributes.
     * 
     * @param mixed  output  An object or array to convert to XML and output.
     * @param mixed  onerror An object or array to convert to XML and output if there are errors with 'output'.
     */
    function setE4XResponse($output, $rootelement = "output",  $encoding = 'utf-8')
    {
        m2import("phpinchains/util/XMLSerializer.php");
        header("Content-Type: application/xml; charset=".$encoding);
        $this->setUseReturnValue(false);
        $ctx = & phpinchains_ApplicationContext::getInstance();
        $out = & $ctx->getOutputWriter();
        $enc = phpinchains_util_XMLSerializer::serializeE4X($output, $rootelement);
        $out->write($enc);
    }
    
    /**
     * Returns true if the return value of executeAction should be used to forward to a view
     * 
     * @return boolean Should return value be used
     */
    function useReturnValue()
    {
        return $this->useReturnValue;
    }

    /**
     * Sets if return value of an action method will be used to find a view
     * 
     * @param bool boolean 
     */
    function setUseReturnValue($bool)
    {
        $this->useReturnValue = $bool;
    }

    /**
     * Delegation method for ActionDispatcherFilter::callAction
     * 
     * Primary used to redirect to actions which are not accessible from the outside.
     * 
     * @see ActionDispatcher#callAction
     */
    function callAction ($actionclass, & $actionform, $method = '')
    {
        $this->setUseReturnValue(false);
        $this->dispatcher->callAction($actionclass, $actionform, $method);
    }

    /**
     * Delegation method for ActionDispatcherFilter::dispatch
     * 
     * Used to dispatch a registered action in the same module.
     * 
     * @param string actionmapping Can be everything that can be used in c:url action
     *      or c:form action; The target action MUST be in the same module
     * @see ActionDispatcher#dispatch
     */
    function dispatch ($actionmapping, & $actionform)
    {
        $this->setUseReturnValue(false);
        $this->dispatcher->dispatch($actionmapping, $actionform);
    }
    
    /**
     * Switch to an other module and provide data via GET.
     * 
     * Used to dispatch a registered action in an other module. The complete actionform
     * will be submitted in the query string over the net, so be CAREFULL!
     * Objects are not submitted, but arrays are in the php way: field[1]=ping
     * Normally it is not necessary to redirect to actions in other modules with providing
     * data to that action. So think twice, before you use this method. If you only want
     * the user be able to switch to the other module, use a link instead. e.g.
     * <code>
     * <a href="<c:url action="someaction" module="somemodule"/>">Some Name</a>
     * </code>
     * 
     * @param string actionmapping Can be everything that can be used in c:url action
     *      or c:form action; The target action MUST be in the same module
     * @see ActionDispatcher#dispatch
     */
    function redirect ($module, $actionmapping, & $actionform)
    {
        $url = new phpinchains_tagsupport_URL($this->env);
        $url->setModule($module);
        $url->setAction($actionmapping);
        $arr = $actionform->getArray();
        if (is_array($arr))
            foreach ($arr as $n => $v)
            {
                if (!is_object($v))
                    if (is_array($v))
                        foreach ($v as $n1 => $v1)
                        {
                            $url->addParam($n.'['.$n1.']', $v1);
                        }
                    else
                        $url->addParam($n, $v);
            }
        $this->useReturnValue = false;
        ob_clean();
        header("Location: ".$url->toString());
    }

    /**
     * Create a link alike c:url in psps programmatically. May for example be used with JSON output.
     * 
     * @param array attrs    Assoc array with same attributes which may be used with c:url tag, except 'var'.
     *                       Namely: value, action, module.
     * @param array params   Array with name value pairs as parameters.
     */
    function createLinkTo($attrs, $params = array())
    {
        $url = null;
        if (isset($attrs['value']))
            $url = new phpinchains_tagsupport_URL($this->env, $attrs['value']);
        else
            $url = new phpinchains_tagsupport_URL($this->env);
        if (isset($attrs['module']))
            $url->setModule($attrs['module']);
        if (isset($attrs['action']))
            $url->setAction($attrs['action']);
        if (is_array($params))
            foreach ($params as $n => $n) {
                $url->addParam($n, $v);
            }
        return $url->toString();
    }
    
    /**
     * Check for security code, which is bound to the user's session.
     * 
     * Prevents attacks, where some user could process commands when an other user simply displays a page.
     * 
     * e.g. <IMG SRC="http://www.thesiteyouareon.com/somecommand.php?somevariables=maliciouscode">
     * 
     * The commands are processed in the context of the user who displays the html page.
     *
     * @return bool Check successful?
     */
    function checkSecCode () {
        $check_code = $this->actionform->get("__ss");
        $code       = $this->env->getSession("phpinchains_sec_code");
        return ($check_code == $code);
    }
    
    /**
     * Convenience method for checking the syncronizer token. 
     *
     * @param string $name
     * @return bool Token valid?
     */
    function checkSyncToken ($name = '') {
    	if (!$name && $form = $this->actionform->get("__formName")) {
   			$name = $form; 
    	}
    	return phpinchains_SynchronizerToken::getInstance($name)->check(); 
    }
    
    /**
     * Get an array with the form data
     * 
     * The user has the possibility to send every data fields he wants. This method
     * returns a clean array with only the fields, which were defined in the form.
     *
     * @param string $form
     * @param string $psp
     * @param boolean $autohandlefile If true, fields '<filefield>_type', '<filefield>_size', and '<filefield>_filename' are automatically filled. Used for input fields of type 'file'. 
     */
    function getFormdata ($form = "", $psp = "", $autohandlefile = false) {
        if (!$form) {
            $form = $this->actionform->get('__formName');
        }
        if (!$psp) {
            $psp = $this->actionform->get('__lastView');
        }
        $pspobject = $this->dispatcher->getPSPObj($psp);
        $data = array();
        if (is_object($pspobject)) {
            $data = DbUtil::cleanDataArray($this->actionform->getArray(), $pspobject, $form, $autohandlefile);
        }
        return $data;
    }
    
    /**
     * Constrain the incoming views of an action method 
     *
     * Arguments: Views allowed as pairs of view name and form name
     * e.g. $this->constrainIncomingView(array("view1", "form1"), array("folder1/view2", "form2"));
     * if only one incoming view is specified the following alternate syntax is also allowed:
     * $this->constrainIncomingView("view1", "form1");
     */
    function constrainIncomingView () {
    	$views = func_get_args();
    	if (!is_array($views[0])) 
    		$views[0] = array($views[0], $views[1]);
        $psp  = $this->actionform->get('__lastView');
        $form = $this->actionform->get('__formName');
        $found = false;
        foreach ($views as $view) {
            if (strcmp($view[0], $psp) == 0 && strcmp($view[1], $form) == 0) {
                $found = true;
                break;
            }
        }
        if (!$found) {
            die("<strong>Fatal security error:</strong> The incoming view '$psp' with form '$form' is not allowed in this context! This incident is beeing reported!");
        }
    }
    
    // TODO schauen wg konsistenz von return value und findForward bei methodmapping
    /**
     * Validate specified form of the given psp page (with or without extension)
     * 
     * <code>   
     * if (!$this->validate($psp, $form)
     *   return $this->dispatch('myfolder/mypsp', $actionform);
     * </code>
     * or
     * <code>   
     * if (!$this->validate($viewmapping->findForward($mapping), $form)
     *   return $this->dispatch('myfolder/mypsp', $actionform);
     * </code>
     * 
     * @param string psp Path to psp relative to view directory
     * @param string form Name of the form to validate 
     */
    function validate ($psp = '', $form = '', $field = '')
    {
        if (!$form) {
            $form = $this->actionform->get('__formName');
        }
        if (!$psp) {
            $psp = $this->actionform->get('__lastView');
        }
        if (!$form || !$psp) {
        	return false;
        }
        if (substr($psp, -4, 4) == '.psp')
            $psp = substr($psp, 0, -4);
        return $this->dispatcher->validate($this->actionform, $psp, $form, $field);
    }
    
    /**
     * Validate specified form field of the given psp page (with or without extension)
     * 
     * <code>   
     * if (!$this->validateField($psp, $form, $field)
     *   return $this->dispatch('myfolder/mypsp', $actionform);
     * </code>
     * or
     * <code>   
     * if (!$this->validateField($viewmapping->findForward($mapping), $form, $field)
     *   return $this->dispatch('myfolder/mypsp', $actionform);
     * </code>
     * 
     * @param string psp   Path to psp relative to view directory
     * @param string form  Name of the form to validate 
     * @param string field Name of the field to validate 
     */
    function validateField ($psp, $form, $field)
    {
		return $this->validate($psp, $form, $field);
    }
    
    /**
     * Handle an AJAX form request (ajax:form) if parameter 'update' is used
     * 
     * e.g. $this->handleAjaxUpdate("my/test", "form", "my::test", "MyAction", "_doSomething");
     * 
     * @param string psp                    Path to PSP page from view root dir
     * @param string form                   Form to validate
     * @param string incomingaction         Action mapping which rendered the form
     * @param string outgoingactionclass    Class of action to call in case of successful validation or action mapping
     * @param string outgoingactionmethod   Method of action to call in case of successful validation; if not given, 
     *                                      outgoingactionclass is handled as action mapping
     */
    function handleAjaxUpdate ($psp, $form, $incomingaction, $outgoingactionclass, $outgoingactionmethod = '') {
        $this->actionform->set("__formselector__", $form);
    	$ctx = & phpinchains_ApplicationContext::getInstance();
        if (!empty($form) && !empty($psp) && !$this->validate($psp, $form))
        {
            header("HTTP/1.1 409 Conflict");
            ob_start(create_function('$buffer', 'return "{ \"form\" : \"".str_replace("\n", " ", str_replace(\'"\', \'\"\', $buffer))."\"";'));
            $this->dispatch($incomingaction, $this->actionform);
            ob_end_flush();
            print "}";
    		$ctx->log('WebAction::handleUpdate HTTP 409 (ajax form not valid) => '.$incomingaction);
            return;
        }
    	$ctx->debug('WebAction::handleUpdate: ajax form valid => '.$outgoingactionclass.'::'.$outgoingactionmethod);
    	ob_start(create_function('$buffer', 'return "{ \"form\" : \"".str_replace("\n", " ", str_replace(\'"\', \'\"\', $buffer))."\"";'));
    	$this->dispatch($incomingaction, $this->actionform);
        ob_end_flush();
        // clear output writer buffers
        $out   = & $ctx->getOutputWriter();
        $out->clearBuffer();
        $jsout = & $ctx->getOutputWriter();
        $jsout->clearBuffer();
        ob_start(create_function('$buffer', 'return ",\"result\" : \"".str_replace("\n", " ", str_replace(\'"\', \'\"\', str_replace("\'", "\\\'", $buffer)))."\"}";'));
        if ($outgoingactionmethod)
            $this->callAction($outgoingactionclass, $this->actionform, $outgoingactionmethod);
        else
            $this->dispatch($outgoingactionclass, $this->actionform);
        ob_end_flush();
        return;
    }

    /**
     * Helper method for easily creating a wizzard
     * 
     * Usage:
     * <code>
     * $this->wizzard($env, $actionform, 
     *  array(
     *      array("action/psp1", "formname1"),
     *      array("action/psp2", "formname2"),
     *      array("action/psp3", "formname3"),
     *      array("action/psp4", "formname4", "aMethod")
     *      [, ...]
     *  ), "sessionprefix"[, "initMethod"]
     * );
     * </code>
     * The first two elements of the inner arrays are equal to the two parameters of
     * the validate method. The third element is the name of an optional callback
     * method in the same action, which is called if the validation of the form was 
     * successful. The signature of the method has to be:
     * <code> 
     * function &lt;methodname&gt;(&$env, &$actionform[, $sessionprefix[, $step]]);
     * </code>
     * The complete data the dialog gathered, is now accessible through 
     * $env->getSession("sessionprefix_form"). The last view doesn't need
     * a form name. It may have a 'complete' button for example and the called 
     * action may store the data somewhere and remove the entry from the session
     * ($env->removeSession("sessionprefix_form")).
     * The forms on the PSPs for the steps must have a hidden field named 'doSend'
     * with any but not empty value. To step forward a submit button is needed with
     * any or no name. The back button (not in the first PSP) has to be named
     * 'stepBack'.
     * The session variable $sessionprefix."_form" has to be removed in the callback 
     * method of the last tripel or in the action called after the last view 
     * ($env->removeSession($sessionprefix."_form")).
     * 
     * @param env $env Envionment wrapper to reduce coupling with so called magic vars
     * @param ActionForm $actionform The incoming action form with the request parameters
     * @param array $wizzard An array of array with pairs of psp paths and form names
     * @param string $finalview Last view, shown after form was send or wizzard was 
     *          finished, respectively.
     * @param string $sessionprefix Prefix used to store internal data of a wizzard
     *          and the collected form data
     */
    function wizzard (& $env, & $actionform, $wizzard, $finalview, $sessionprefix)
    {
        $wizzard[] = array($finalview, '', '');
        if ($env->getSession($sessionprefix) < 0)
            $env->setSession($sessionprefix, 0);
        if ($actionform->get("stepBack") && -1+(int)$env->getSession($sessionprefix) > -1)
            $env->setSession($sessionprefix, -1+(int)$env->getSession($sessionprefix));
        if (!$env->getSession($sessionprefix) && !$actionform->get("doSend"))
        {
            if ($init != null)
                $this->$init($env, $actionform, $sessionprefix, 0);
            return '/'.$wizzard[0][0];
        }
        else
        {
            if ($env->getSession($sessionprefix) >= count($wizzard)-1) {
                $env->removeSession($sessionprefix);
            }
            $page = '/'.$wizzard[(int) $env->getSession($sessionprefix)][0];
            if ($actionform->get("doSend"))
            {
                if (!$actionform->get('stepBack') 
                    && !$this->validate($wizzard[(int) $env->getSession($sessionprefix)][0],
                    $wizzard[(int) $env->getSession($sessionprefix)][1]))
                {
                    $page = '/'.$wizzard[(int) $env->getSession($sessionprefix)][0];
                }
                else
                {
                    if ($env->getSession($sessionprefix) < count($wizzard)-1 
                        && !$actionform->get("stepBack"))
                        $env->setSession($sessionprefix, 1+(int)$env->getSession($sessionprefix));
                    $step_form_array = $env->getSession($sessionprefix."_form");
                    $step_form = new phpinchains_ActionForm();
                    $step_form->clear();
                    if (is_array($step_form_array))
                    {
                        $step_form->setArray($step_form_array);
                    }
                    $offset = $actionform->get("stepBack") ? +1 : -1;
                    $obj = $this->dispatcher->getPSPObj($wizzard[(int) $env->getSession($sessionprefix)+$offset][0]);
                    $fields = $obj->getFormFields($wizzard[(int) $env->getSession($sessionprefix)+$offset][1]);
                    if (is_array($fields))
                        foreach ($fields as $field)
                        {
                            $step_form->set($field, $actionform->get($field));
                        }
                    $env->setSession($sessionprefix."_form", $step_form->getArray());
                    $actionform->setArray($step_form->getArray());
                    if (isset($wizzard[-1 + (int) $env->getSession($sessionprefix)][2]) 
                        && ($func = $wizzard[-1 + (int) $env->getSession($sessionprefix)][2])
                        && !$actionform->get("stepBack"))
                    {
                        $this->$func($env, $actionform, $sessionprefix, 
                            (int) $env->getSession($sessionprefix));
                    }
                    $page = '/'.$wizzard[(int) $env->getSession($sessionprefix)][0];
                }
            }
            return $page;
        }
    }

    /**
     * Short cut method for copying value from actionform to response scope (env attribute)
     * 
     * @param mixed key Key or array keys of the value(s) to copy
     * @param boolean assoz Array is assoziative. Array key will be used to get value from 
     *      actionform and value will be used to store it in response scope
     */
    function af2attr ($key, $assoz = false)
    {
        if (is_array($key))
        {
            if ($assoz)
            {
                foreach ($key as $n => $v)
                    $this->env->setAttribute($v, $this->actionform->get($n));
            }
            else
            {
                foreach ($key as $k)
                    $this->env->setAttribute($k, $this->actionform->get($k));
            }        
        }
        else
            $this->env->setAttribute($key, $this->actionform->get($key));
    }

    /**
     * Short cut method for copying value from actionform to response scope (env attribute)
     * 
     * @param mixed key Key or array keys of the value(s) to copy
     * @param boolean assoz Array is assoziative. Array key will be used to get value from 
     *      actionform and value will be used to store it in responce scope
     */
    function af2zombie ($key, $assoz = false)
    {
        if (is_array($key))
        {
            if ($assoz)
            {
                foreach ($key as $n => $v)
                    $this->env->setZombie($v, $this->actionform->get($n));
            }
            else
            {
                foreach ($key as $k)
                    $this->env->setZombie($k, $this->actionform->get($k));
            }
        }
        else
            $this->env->setZombie($key, $this->actionform->get($key));
    }

}
?>